package com.asset.dispoal.logger;

import com.alibaba.fastjson2.JSON;
import com.asset.dispoal.logger.annoation.SysLog;
import com.asset.dispoal.logger.entity.EsLogger;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import tech.mhuang.pacebox.core.date.DateTimeUtil;
import tech.mhuang.pacebox.core.dict.BasicDict;
import tech.mhuang.pacebox.core.id.BaseIdeable;
import tech.mhuang.pacebox.core.util.CollectionUtil;
import tech.mhuang.pacebox.core.util.ObjectUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;

/**
 * 系统日志记录
 *
 * @author mhuang
 * @since 1.0.0
 */
@Aspect
@Order
@Slf4j
public class SysLogAspect {

    @Autowired
    private BaseIdeable<String> ideable;

    @Autowired
    private Environment environment;

    @Pointcut("@annotation(com.asset.dispoal.logger.annoation.SysLog)")
    private void sysLog() {
    }

    private ThreadLocal<EsLogger> loggerThreadLocal = ThreadLocal.withInitial(() -> null);

    @Before("sysLog()")
    public void doBefore(JoinPoint joinPoint) throws ClassNotFoundException {
        EsLogger logger = initPacklogger(joinPoint);

        //接收到请求，记录请求内容,先判断是不是web的spring应用，若是其他请自行修改
        Class.forName("org.springframework.web.context.request.RequestContextHolder");
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (ObjectUtil.isNotEmpty(attributes)) {
            HttpServletRequest request = attributes.getRequest();
            packServletLogger(request, logger);
        }
        loggerThreadLocal.set(logger);
    }

    @AfterReturning(returning = "ret", pointcut = "sysLog()")
    public void doAfterReturning(JoinPoint joinPoint, Object ret) {
        EsLogger logger = loggerThreadLocal.get();
        packRestLogger(logger, "1");
        MethodSignature method = (MethodSignature) joinPoint.getSignature();
        SysLog sysLog = getAnnoationByMethod(method);
        if (!sysLog.ignoreRestData()) {
            logger.setRespData(JSON.toJSONString(ret));
        }
        log.debug("日志应答的数据是:{}", logger);
    }

    @AfterThrowing(value = "sysLog()", throwing = "ex")
    public void doAfterThrowingAdvice(JoinPoint joinPoint, Throwable ex) {
        EsLogger logger = loggerThreadLocal.get();
        packRestLogger(logger, "2");
        logger.setErrorMsg(ex.getMessage());
        MethodSignature method = (MethodSignature) joinPoint.getSignature();
        SysLog sysLog = getAnnoationByMethod(method);
        if (!sysLog.ignoreErrorData()) {
            StackTraceElement[] stackTraceElements = ex.getStackTrace();
            StringBuilder errDetailMsg = new StringBuilder();
            if (CollectionUtil.isNotEmpty(stackTraceElements)) {
                errDetailMsg.append(logger.getErrorMsg());
                for (StackTraceElement stackTraceElement : stackTraceElements) {
                    errDetailMsg.append(stackTraceElement.toString());
                    errDetailMsg.append(System.lineSeparator());
                }
            }
            logger.setErrorDetailMsg(errDetailMsg.toString());
        }
        log.debug("日志异常的数据是:{}", logger);
    }

    /**
     * 组装应答日志基础
     *
     * @param logger 日志
     * @param type   状态
     */
    private void packRestLogger(EsLogger logger, String type) {
        logger.setRespTime(DateTimeUtil.currentDate());
        logger.setStatus(type);
        logger.setTaskTime(logger.getRespTime().getTime() - logger.getReqTime().getTime());
    }

    /**
     * 初始化日志
     *
     * @param joinPoint 连接点
     */
    private EsLogger initPacklogger(JoinPoint joinPoint) {
        EsLogger logger = new EsLogger();
        logger.setId(ideable.generateId());
        MethodSignature method = (MethodSignature) joinPoint.getSignature();
        logger.setEnv(environment.getProperty("spring.profiles.active"));
        logger.setModule(environment.getProperty("spring.application.name"));
        SysLog sysLog = getAnnoationByMethod(method);
        logger.setName(sysLog.name());
        logger.setType(sysLog.type());
        if (!sysLog.ignoreReqData()) {
            logger.setReqData(getDataByPoint(joinPoint));
        }
        try {
            logger.setOperatorUserId(sysLog.userGetManager().newInstance().getUserId());
        } catch (Exception e) {
            log.error("日志获取用户id异常", e);
        }
        logger.setReqTime(DateTimeUtil.currentDate());
        return logger;
    }

    /**
     * 组装servlet日志
     *
     * @param request servlet
     * @param logger  日志
     * @return 日志对象
     */
    private void packServletLogger(HttpServletRequest request, EsLogger logger) {
        logger.setReqHost(request.getRemoteHost());
        logger.setReqUrl(request.getRequestURL().toString());
        Enumeration<String> headerKey = request.getHeaderNames();
        BasicDict header = new BasicDict(20);
        while (headerKey.hasMoreElements()) {
            String key = headerKey.nextElement();
            header.put(key, request.getHeader(key));
        }
        logger.setReqHeader(JSON.toJSONString(header));
    }

    /**
     * 根据Point获取数据
     *
     * @param joinPoint point
     * @return data数据
     */
    private String getDataByPoint(JoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        String data = null;
        //组装传输对象数组
        if (CollectionUtil.isNotEmpty(args)) {
            try {
                data = JSON.toJSONString(args);
                data = data.replaceAll("\\\\", "");
            } catch (Exception e) {
                log.error("记录日志获取请求数据异常", e);
            }
        }
        return data;
    }

    /**
     * 获取方法上的注解
     *
     * @param methodSignature 方法签名
     * @return 注解
     */
    private SysLog getAnnoationByMethod(MethodSignature methodSignature) {
        Method method0 = methodSignature.getMethod();
        return method0.getAnnotation(SysLog.class);
    }
}